﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;

namespace MentalAlchemy.Molecules.Image
{
	/// <summary>
	/// Class for implementation of image specific maths.
	/// </summary>
	public class ImageMaths
	{
		#region - Logical operations. -
		/// <summary>
		/// [molecule]
		/// 
		/// Performs logical OR (max) for the given images.
		/// </summary>
		/// <param name="src1">1st argument image.</param>
		/// <param name="src2">2nd argument image.</param>
		/// <returns>Result of the operation.</returns>
		public static Bitmap LogicalOR(Bitmap src1, Bitmap src2)
		{
			var resImg = new Bitmap(src1);

			var rect = new Rectangle(0, 0, src1.Size.Width, src1.Size.Height);
			BitmapData bmpData1 = resImg.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb),
						bmpData2 = src2.LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);

			int bytesSize = resImg.Height * bmpData1.Stride;
			byte[] bytes1 = new byte[bytesSize],
					bytes2 = new byte[bytesSize];

			System.Runtime.InteropServices.Marshal.Copy(bmpData1.Scan0, bytes1, 0, bytesSize);
			System.Runtime.InteropServices.Marshal.Copy(bmpData2.Scan0, bytes2, 0, bytesSize);

			for (int i = 0; i < bytesSize; ++i)
			{
				bytes1[i] = Math.Max(bytes1[i], bytes2[i]);
			}

			System.Runtime.InteropServices.Marshal.Copy(bytes1, 0, bmpData1.Scan0, bytesSize);
			resImg.UnlockBits(bmpData1);
			src2.UnlockBits(bmpData2);

			return resImg;
		}
		#endregion

		#region - Morphological operations. -
		/// <summary>
		/// [molecule]
		/// 
		/// Performs basic dilation with 3x3 'all-blacks' window.
		/// </summary>
		/// <returns>Dilated image.</returns>
		public static Bitmap Dilate(Bitmap src, byte whiteLvl, byte blackLvl)
		{
			var table = ImageConversion.ConvertToGrayscaleTable(src);
			var resTable = Dilate(table, whiteLvl, blackLvl);
			return ImageConversion.CreateBmpFromGrayscaleTable(resTable);
		}

		/// <summary>
		/// [molecule]
		/// 
		/// Performs basic dilation (min) with 3x3 'all-blacks' window.
		/// </summary>
		/// <returns>Dilated image.</returns>
		public static byte[,] Dilate(byte[,] table, byte whiteLvl, byte blackLvl)
		{
			int width = table.GetLength(1), height = table.GetLength(0);
			var res = new byte[height,width];

			for (int i = 1; i < height; i++)
			{
				res[i, 0] = whiteLvl;
				for (int j = 1; j < width-1; j++)
				{
					if (table[i - 1, j - 1] == blackLvl || table[i - 1, j] == blackLvl || table[i - 1, j + 1] == blackLvl
						|| table[i, j - 1] == blackLvl || table[i, j] == blackLvl || table[i, j + 1] == blackLvl
						|| table[i + 1, j - 1] == blackLvl || table[i + 1, j] == blackLvl || table[i + 1, j + 1] == blackLvl)
					{
						res[i,j] = blackLvl;
					}
					else
					{
						res[i, j] = whiteLvl;
					}
				}
				res[i, width-1] = whiteLvl;
			}

			//
			// fill borders with [whiteLvl].
			for (int i = 0; i < width; ++i)
			{
				res[0, i] = whiteLvl;
				res[height-1, i] = whiteLvl;
			}
			for (int i = 1; i < height - 1; ++i)
			{
				res[i, 0] = whiteLvl;
				res[i, width - 1] = whiteLvl;
			}

			return res;
		}

		/// <summary>
		/// [molecule]
		/// 
		/// Performs basic erosion (max) with 3x3 'all-blacks' window.
		/// </summary>
		/// <returns>Eroded image.</returns>
		public static Bitmap Erode(Bitmap src, byte whiteLvl, byte blackLvl)
		{
			var table = ImageConversion.ConvertToGrayscaleTable(src);
			var resTable = Erode(table, whiteLvl, blackLvl);
			return ImageConversion.CreateBmpFromGrayscaleTable(resTable);
		}

		/// <summary>
		/// [molecule]
		/// 
		/// Performs basic erosion (max) with 3x3 'all-blacks' window.
		/// </summary>
		/// <returns>Eroded image.</returns>
		public static byte[,] Erode(byte[,] table, byte whiteLvl, byte blackLvl)
		{
			int width = table.GetLength(1), height = table.GetLength(0);
			var res = new byte[height,width];

			for (int i = 1; i < height - 1; ++i)
			{
				for (int j = 1; j < width - 1; ++j)
				{
					if (table[i, j] == Const.GRAYLEVEL_MIN
						&& table[i - 1, j - 1] == Const.GRAYLEVEL_MIN && table[i - 1, j] == Const.GRAYLEVEL_MIN && table[i - 1, j + 1] == Const.GRAYLEVEL_MIN
						&& table[i, j - 1] == Const.GRAYLEVEL_MIN && table[i, j + 1] == Const.GRAYLEVEL_MIN
						&& table[i + 1, j - 1] == Const.GRAYLEVEL_MIN && table[i + 1, j] == Const.GRAYLEVEL_MIN && table[i + 1, j + 1] == Const.GRAYLEVEL_MIN)
					{
						res[i, j] = blackLvl;
					}
					else
					{
						res[i, j] = whiteLvl;
					}
				}
			}

			//
			// fill borders with [whiteLvl].
			for (int i = 0; i < width; ++i)
			{
				res[0, i] = whiteLvl;
				res[height - 1, i] = whiteLvl;
			}
			for (int i = 1; i < height - 1; ++i)
			{
				res[i, 0] = whiteLvl;
				res[i, width - 1] = whiteLvl;
			}

			return res;
		}
		#endregion
	}
}

